/* @flow */

import {escape, isSSRUnsafeAttr} from 'web/server/util'
import {isObject, extend} from 'shared/util'
import {renderAttr} from 'web/server/modules/attrs'
import {renderClass} from 'web/util/class'
import {genStyle} from 'web/server/modules/style'
import {normalizeStyleBinding} from 'web/util/style'

import {
  normalizeChildren,
  simpleNormalizeChildren
} from 'core/vdom/helpers/normalize-children'

import {
  propsToAttrMap,
  isRenderableAttr
} from 'web/server/util'

const ssrHelpers = {
  _ssrEscape: escape,
  _ssrNode: renderStringNode,
  _ssrList: renderStringList,
  _ssrAttr: renderAttr,
  _ssrAttrs: renderAttrs,
  _ssrDOMProps: renderDOMProps,
  _ssrClass: renderSSRClass,
  _ssrStyle: renderSSRStyle
}

export function installSSRHelpers(vm: Component) {
  if (vm._ssrNode) {
    return
  }
  let Vue = vm.constructor
  while (Vue.super) {
    Vue = Vue.super
  }
  extend(Vue.prototype, ssrHelpers)
  if (Vue.FunctionalRenderContext) {
    extend(Vue.FunctionalRenderContext.prototype, ssrHelpers)
  }
}

class StringNode {
  isString: boolean;
  open: string;
  close: ?string;
  children: ?Array<any>;

  constructor(
    open: string,
    close

?:
  string
,
  children
?:

  Array<any>

,
  normalizationType
?:
  number
) {
  this
.
  isString = true
  this
.
  open = open
  this
.
  close = close

  if(children) {
    this.children = normalizationType === 1
      ? simpleNormalizeChildren(children)
      : normalizationType === 2
        ? normalizeChildren(children)
        : children
  }

  else {
  this
.
  children = void 0
}
}
}

function renderStringNode(
  open: string,
  close

? : string,
  children ? : Array < any >,
  normalizationType ? : number
):
StringNode
{
  return new StringNode(open, close, children, normalizationType)
}

function renderStringList(
  val: any,
  render: (
    val: any,
    keyOrIndex: string | number,
    index?: number

) =>
string
):
string
{
  let ret = ''
  let i, l, keys, key
  if (Array.isArray(val) || typeof val === 'string') {
    for (i = 0, l = val.length; i < l; i++) {
      ret += render(val[i], i)
    }
  } else if (typeof val === 'number') {
    for (i = 0; i < val; i++) {
      ret += render(i + 1, i)
    }
  } else if (isObject(val)) {
    keys = Object.keys(val)
    for (i = 0, l = keys.length; i < l; i++) {
      key = keys[i]
      ret += render(val[key], key, i)
    }
  }
  return ret
}

function renderAttrs(obj: Object): string {
  let res = ''
  for (const key in obj) {
    if (isSSRUnsafeAttr(key)) {
      continue
    }
    res += renderAttr(key, obj[key])
  }
  return res
}

function renderDOMProps(obj: Object): string {
  let res = ''
  for (const key in obj) {
    const attr = propsToAttrMap[key] || key.toLowerCase()
    if (isRenderableAttr(attr)) {
      res += renderAttr(attr, obj[key])
    }
  }
  return res
}

function renderSSRClass(
  staticClass: ?string,
  dynamic: any
): string {
  const res = renderClass(staticClass, dynamic)
  return res === '' ? res : ` class="${escape(res)}"`
}

function renderSSRStyle(
  staticStyle: ?Object,
  dynamic: any,
  extra: ?Object
): string {
  const style = {}
  if (staticStyle) extend(style, staticStyle)
  if (dynamic) extend(style, normalizeStyleBinding(dynamic))
  if (extra) extend(style, extra)
  const res = genStyle(style)
  return res === '' ? res : ` style=${JSON.stringify(escape(res))}`
}
